#pragma once

#include "OrionFlames.h"
#include "Preset.h"

namespace OrionFlames
{
class OFAPI IfsRenderer
{
public:
	IfsRenderer()
	{
		//This must be initialized before any program stuff is done, but there's probably a better place for it.
		glewInit();
		m_PlaneProg = MakePlaneProgram();//May need to guard with a bool - Matt.
	}

	~IfsRenderer()
	{
		//ClearPresets();
		glDeleteObjectARB(m_PlaneProg);
	}

	//This function is something we really want to get rid of as it's tightly coupling the rendering of the IFS output texture
	//to how it's drawn.
	static void Draw(Preset* originalPreset, Preset* currentPreset, bool newPreset, int width, int height)
	{
		GLuint texIFS = currentPreset->SimulateIFS(newPreset);//The most important part.
		
		glViewport(0, 0, width, height);
		glEnable(GL_DEPTH_TEST);
		glDisable(GL_BLEND);
	
		//Do normal fullscreen rendering.
		glMatrixMode(GL_PROJECTION);//Set up projection matrix.
		glLoadIdentity();//Reset matrix at start of frame.
		gluPerspective(90.0,//Y camera field-of-view, in degrees.
					   width / (float)height,//Aspect ratio: width over height.
					   0.01,//Near clipping plane depth.
					   100.0);//Far clipping plane depth.

		oglCameraLookAt(1.0);//Should probably change this to a glu call - Matt.

		glMatrixMode(GL_MODELVIEW);//Set up model and view matrix.
		glLoadIdentity();//Reset matrix at start of frame.
		
		glClearColor((GLclampf)0.3, (GLclampf)0.5, (GLclampf)0.7, 0);//Set background color to blue. Perhaps make this configurable - Matt.
		glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
		glEnable(GL_DEPTH_TEST);
		
		//Draw the current output texture.
		glBindTexture(GL_TEXTURE_2D, texIFS);
		glEnable(GL_TEXTURE_2D); 
		glColor4f(1, 1, 1, 1);
		//glEnable(GL_BLEND);//These were commented out in the original - Matt.
		//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
	    glUseProgramObjectARB(m_PlaneProg);
		glUniformTextures(m_PlaneProg, originalPreset->TexScale(), originalPreset->TexScalei());
		glFastUniform1f(m_PlaneProg, "colormode", originalPreset->Exposure() < 0 ? (float)0.0 : (float)1.0);//Figure out background changing later. Keep fixed relative to original exposure value for now - Matt.
		float gs = 100;//Ground plane dimensions: over 1000 gives roundoff.
		DrawTexture(mat4(1.0), vec3(-gs, -gs, 0.5), vec3(gs, gs, 0.5));
	    glUseProgramObjectARB(0);

		//Can't have this if calling from outside.
		//glutSwapBuffers();//Copy back buffer to front.
	}

private:
	static GLhandleARB m_PlaneProg;
	static string m_PlanePixelShaderText;
	static string m_PlaneVertexShaderText;
	
	static GLhandleARB MakePlaneProgram()
	{
		m_PlaneVertexShaderText = STRINGIFY
		(
			// GLSL Vertex shader:
			varying vec3 worldCoords;
			void main(void)
			{
				worldCoords = vec3(gl_ModelViewMatrix * gl_Vertex);
				gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; 
			}
		);

		m_PlanePixelShaderText = CoordMap::GLSLTexPlaneString() + STRINGIFY
		(
			// GLSL Fragment shader
			varying vec3 worldCoords;
			uniform sampler2D tex;
			uniform float colormode;
	
			void main(void)
			{
				//flip display Y coordinate, to match Apophysis rendering.
				vec2 texcoords = TXfmPL(vec2(worldCoords.x, -worldCoords.y));
				//texcoords=vec2(worldCoords); // for debugging texture generation
				vec4 t = texture2D(tex, texcoords);
				gl_FragColor = t * colormode + (1.0 - t) * (1.0 - colormode);
				gl_FragColor.a = t.a;//Copy alpha unmodified.
			}
		);

		return makeProgramObject(m_PlaneVertexShaderText.c_str(), m_PlanePixelShaderText.c_str());
	}
};
}